package com.songlanyun.modules.studying.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.songlanyun.common.exception.RRException;
import com.songlanyun.common.utils.*;
import com.songlanyun.common.utils.custom.MathUtils;
import com.songlanyun.modules.studying.dao.StudyingDao;
import com.songlanyun.modules.studying.entity.Studying;
import com.songlanyun.modules.studying.model.StudyingVO;
import com.songlanyun.modules.studying.service.StudyingService;
import com.songlanyun.modules.studyingDetail.entity.StudyingDetail;
import com.songlanyun.modules.studyingDetail.service.StudyingDetailService;
import com.songlanyun.modules.studyingLately.dao.StudyingLatelyDao;
import com.songlanyun.modules.studyingLately.entity.StudyingLately;
import com.songlanyun.modules.studyingType.service.StudyingTypeService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

@Service("studyingService")
public class StudyingServiceImpl extends ServiceImpl<StudyingDao, Studying> implements StudyingService {

    @Autowired
    private StudyingTypeService studyingTypeService;

    @Autowired
    private StudyingDetailService studyingDetailService;

    @Resource
    private StudyingLatelyDao studyingLatelyDao;


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<Studying> page = this.baseMapper.queryPage(new Query<Studying>().getPage(params), params);

        return new PageUtils(page);
    }

    @Override
    public Studying loadById(Long id, boolean b) {
        Studying studying = this.baseMapper.selectById(id);
        if (ObjectUtil.isNull(studying) && b) {
            throw new RRException("未找到该课程");
        }
        return studying;
    }

    @Override
    public Studying add(Studying studying) {
        //看课程分类存不存在
        studyingTypeService.loadById(studying.getStudyType(), true);
        loadByName(studying);
        this.baseMapper.insert(studying);
        return studying;
    }

    @Override
    public Studying update(Studying studying) {
        //看课程分类存不存在
        studyingTypeService.loadById(studying.getStudyType(), true);
        loadByName(studying);
        this.updateById(studying);
        return studying;
    }

    @Override
    public void headerStatus(Integer id, String type) {
        Studying studying = this.baseMapper.selectById(id);
        //若果课程分类不存在，就不让操作(课程下架后把分类删了，现在又启用课程)
        studyingTypeService.loadById(studying.getStudyType(), true);
        switch (type) {
            case "hasStatus":
                studying.setHasStatus(!studying.getHasStatus());
                break;
            case "hasHeader":
                studying.setHasHeader(!studying.getHasHeader());
                break;
            default:
                throw new RRException("未找到要执行的操作");
        }
        this.baseMapper.updateById(studying);
    }

    @Override
    public List<Studying> studyingLately(Long apiUserId, Integer limit) {
        return this.baseMapper.studyingLately(apiUserId, limit);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public StudyingVO info(Long id, Long userId) {
        //查询课程
        Studying studying = this.loadById(id, true);
        if (!studying.getHasStatus()) {
            throw new RRException("该课程已下架");
        }
        StudyingVO vo = BeanUtil.toBean(studying, StudyingVO.class);

        //查询课程下所有章节
        List<StudyingDetail> list = studyingDetailService.loadByStudyingId(id);
        if (CollectionUtil.isNotEmpty(list)) {
            vo.setStudyingDetails(list);
            vo.setStudyingDetailCount(list.size());
            vo.setStudyingClick(list.stream().mapToInt(StudyingDetail::getVideoClick).sum());
            vo.setStudyingLength(MathUtils.setScale(list.stream().mapToDouble(o -> o.getVideoLength().doubleValue()).sum()));
        }

        //记录用户最近观看
        //查询该用户是否观看此课程
        StudyingLately studyingLately = studyingLatelyDao.selectOne(
                new LambdaQueryWrapper<StudyingLately>()
                        .eq(StudyingLately::getUserId, userId)
                        .eq(StudyingLately::getStudyingId, id)
                        .last("limit 1")
        );
        if (ObjectUtil.isNotNull(studyingLately)) {
            studyingLately.setCreateTime(new Date());
            studyingLatelyDao.updateById(studyingLately);
        } else {
            studyingLately = new StudyingLately();
            studyingLately.setUserId(userId);
            studyingLately.setStudyingId(id);
            studyingLately.setCreateTime(new Date());
            studyingLatelyDao.insert(studyingLately);
        }
        return vo;
    }

    @Override
    public PageUtils studyingLatelyPage(Map<String, Object> params) {
        IPage<Studying> page = this.baseMapper.studyingLatelyPage(new Query<Studying>().getPage(params), params);
        return new PageUtils(page);
    }


    private void loadByName(Studying studying) {
        Integer integer = this.baseMapper.selectCount(new LambdaQueryWrapper<Studying>()
                .eq(Studying::getStudyType, studying.getStudyType())
                .eq(Studying::getStudyName, studying.getStudyName())
                .ne(ObjectUtil.isNotNull(studying.getId()), Studying::getId, studying.getId()));
        if (integer > 0) {
            throw new RRException("该课程分类已有该课程名称");
        }
    }

    @Autowired
    private RedisUtils redisUtils;

    /**
     * 记录用户最近观看
     */
    private void addRecentlyWatched(Long userId, Long studyingId, Integer limit) {
        String userKey = RedisKeys.getRecentlyWatched(userId.toString());
        String studyingKey = RedisKeys.getRecentlyWatched(userId + ":" + studyingId);

        //查询用户是否观看过该课程
        //查询缓存
        String recentlyWatchedDate = redisUtils.getCacheObject(studyingKey);

        if (StringUtils.isEmpty(recentlyWatchedDate)) {
            //如果缓存中不存在，则查询当前用户缓存用户的数量有没有超过限定值
            Collection<String> keys = redisUtils.keys(userKey + ":*");
            if (keys.size() >= limit) {
                //超过了限定值，则将剔除最后一个，存入数据库，然后将此数据存到缓存
                String lastKey = "";
                long lateTimeMillis = System.currentTimeMillis();
                for (String key : keys) {
                    //获取时间
                    String dateStr = redisUtils.get(key);
                    Date date = DateUtil.parseDate(dateStr, DateUtils.DATE_TIME_PATTERN);
                    if (date.getTime() < lateTimeMillis) {
                        lastKey = key;
                        lateTimeMillis = date.getTime();
                    }
                }

                String lastDate = redisUtils.getCacheObject(lastKey);
                //获取用户和课程
                String[] lastArr = lastKey.split(":");
                Long lastUserId = Long.valueOf(lastArr[3]);
                Long lastStudyingId = Long.valueOf(lastArr[4]);
                //查询数据库
                StudyingLately studyingLately = studyingLatelyDao.selectOne(
                        new LambdaQueryWrapper<StudyingLately>()
                                .eq(StudyingLately::getUserId, lastUserId)
                                .eq(StudyingLately::getStudyingId, lastStudyingId)
                                .last("limit 1")
                );
                //存入数据库
                if (ObjectUtil.isNotNull(studyingLately)) {
                    studyingLately.setCreateTime(DateUtil.parseDate(lastDate, DateUtils.DATE_TIME_PATTERN));
                    studyingLatelyDao.updateById(studyingLately);
                } else {
                    studyingLately = new StudyingLately();
                    studyingLately.setUserId(lastUserId);
                    studyingLately.setStudyingId(lastStudyingId);
                    studyingLately.setCreateTime(DateUtil.parseDate(lastDate, DateUtils.DATE_TIME_PATTERN));
                    studyingLatelyDao.insert(studyingLately);
                }

                //剔除时间最靠前的数据
                redisUtils.delete(lastKey);
            }
        }

        //存入缓存
        redisUtils.set(studyingKey, DateUtil.getNowTime(DateUtils.DATE_TIME_PATTERN));

        //输出
        Collection<String> keys = redisUtils.keys(userKey + ":*");
        keys.forEach(key -> {
            //获取课程
            String[] lastArr = key.split(":");
            long lastStudyingId = Long.parseLong(lastArr[4]);
            System.out.println("最近观看【课程：" + lastStudyingId + "，时间：" + redisUtils.get(key) + "】");
        });

    }

    /**
     * 课程删除禁用更新用户最近观看
     */
    private void updateRecentlyWatched(Long studyingId) {
        //查询缓存中所有关于此课程的记录
        Collection<String> keys = redisUtils.keys(RedisKeys.getRecentlyWatched("*:" + studyingId));
        if (CollectionUtil.isNotEmpty(keys)) {
            keys.forEach(key -> {
                String[] arr = key.split(":");
                Long userId = Long.valueOf(arr[3]);
                //移除当前缓存数据
                redisUtils.delete(key);
                //添加新数据到缓存
                //查询该用户再数据库中最新的一条记录
                StudyingLately studyingLately = studyingLatelyDao.selectOne(
                        new LambdaQueryWrapper<StudyingLately>()
                                .eq(StudyingLately::getUserId, userId)
                                .orderByDesc(StudyingLately::getCreateTime)
                                .last("limit 1")
                );
                if (ObjectUtil.isNotNull(studyingLately)) {
                    redisUtils.set(RedisKeys.getRecentlyWatched(studyingLately.getId() + ":" + studyingId), DateUtil.formatData(studyingLately.getCreateTime(), DateUtils.DATE_TIME_PATTERN));
                }

            });
        }
    }

    /**
     * 获取用户最近观看
     */
    @Override
    public List<Studying> getRecentlyWatched(Long userId) {
        Collection<String> keys = redisUtils.keys(RedisKeys.getRecentlyWatched(userId + ":*"));
        List<Studying> list = new ArrayList<>();

        for (String key : keys) {
            //获取课程
            String[] arr = key.split(":");
            long studyingId = Long.parseLong(arr[4]);
            Studying studying = this.baseMapper.selectById(studyingId);

            System.out.println("最近观看【课程：" + studyingId + "，时间：" + redisUtils.get(key) + "】");
            //获取时间
            String dateStr = redisUtils.get(key);
            Date date = DateUtil.parseDate(dateStr, DateUtils.DATE_TIME_PATTERN);
            studying.setUpdateTime(date);
            list.add(studying);
        }

        //排序
        list.sort(Comparator.comparing(Studying::getUpdateTime).reversed());
        return list;
    }
}
